/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package tesauro;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Almacena en una tabla hash palabras y sus sinonimos.
 * 
 * @author demian
 */
public class DiccionarioSinonimos implements Serializable{
    private TablaHash diccionario;
    transient private IObserver observer;

    public DiccionarioSinonimos() {
        //un diccionario bastante completo tiene alrededor de 35000 palabras.
        //numeros primos en el tamaño de la tabla funcionan mejor y algunos vienen al
        //pelo con la funcion de hash nativa de java, por lo que el tamano es todo un tema.
        this(33533);
    }

    public DiccionarioSinonimos(int tamano) {
        diccionario = new TablaHash(tamano);
    }

    public TablaHash getDiccionario() {
            return diccionario;
    }

    /**
     * Agrega un asinonimo a una palabra. Si la palabra no existe
     * ninguna accion se lleva a cabo. Si el sinonimo ya existe en la
     * lista de sinonimos ninguna accion se lleva a acbo.
     * 
     * @param palabra palabra a la cual agrgar un sinonimo.
     * @param sinonimo sinonimo a agregar.
     */
    public void addSinonimo(String palabra, String sinonimo) {
       EntradaDiccionario entrada = diccionario.get(palabra);
       entrada.addSinonimo(sinonimo);
    }

    /**
     * Cambia la funcion hash por defecto por una definida por el usuario. Cada
     * vez que se cambie el algoritmo se reprosesan todos las entradas del diccionario.
     * 
     * @param hashCodeStrategy implementacion de algoritmo de hash.
     */
    public void setFuncionHash(IHashCodeStrategy hashCodeStrategy) {
        diccionario.setHashCodeStrategy(hashCodeStrategy);
        if(observer != null) {
            observer.update(this);
        }         
    }
     
    /**
     * Obtiene la funcion hash que esta
     * usando el diccionario.
     * @return instancia de una implementacion de algoritmo hash.s
     */
    public IHashCodeStrategy getFuncionHash() {
        return diccionario.getHashCodeStrategy(); 
    }
    
    /**
     * Carga al diccionario usando el cargador masivo.
     */
    public void cargarMasivamente(ICargaMasivaDiccionario cargadorMasivo) throws Exception {
        cargadorMasivo.cargarDiccionario(this);
        if(observer != null) {
            observer.update(this);
        }
    }   
    
    /**
     * Añade al diccionario una palabra y su lista de sinonimos. Si la
     * palabra ya existe ninguna accion se lleva a cabo.
     * 
     * @param palabra palabra a añadir.
     * @param sinonimos lista de sinonimos de la palabra.
     * @thorws IllegalArgumentEception si palabra es null.
     */
    public  void add(String palabra, List<String> sinonimos) {
        if(palabra == null) {
            throw new IllegalArgumentException("parametro palabra no puede ser nulo");
        }
        
        diccionario.put(palabra, sinonimos);
        
        if(observer != null) {
            observer.update(this);
        }
    }
    
    /**
     * Verifica que la palabra exista en el diccionario.
     * 
     * @param palabra palabra a ver si existe en el diccionario.
     * @return true si existe, false si no existe.
     */
    public boolean contains(String palabra) {
        return diccionario.contains(palabra);
    }

    public boolean estaVacio() {
        return diccionario.size() == 0 ? true : false;
    }

    /**
     * Obtiene el actual factor de carga del diccionario.
     * 
     * @return factor de carga actual.
     */
    public float getFactorDeCarga() {
       return diccionario.getActualLoadFactor();
    }
    
    /**
     * Obtiene las colisiones actuales.
     * 
     * @return cantidad de colisiones actuales.
     */
    public int getColisiones() {
        return diccionario.getCantidadColisiones();
    }
    
    /**
     * Obtiene la cantidad de colisiones luego de un rehash.
     * 
     * @return cantidad de colisiones luego de un rehash.
     */
    public int getColisionesRehash() {
        return diccionario.getCantidadColisonesHash();
    }
    
    /**
     * Añanda la palabra y la lista de sinonimos al diccionario y
     * tambien va añandiendo cada uno de los sinonimos
     * como palabra y sinonimos.
     * 
     * @param palabra palabra a insertar.
     * @param sinonimos su lista de sinonimos.
     */
    public void recursiveAdd(String palabra, List<String> sinonimos) {
        if(palabra == null) {
            throw new IllegalArgumentException("parametro palabra no puede ser nulo");
        }
        
        diccionario.put(palabra, sinonimos);
        
        ArrayList<String> tmpSinonimos = new ArrayList<String>(sinonimos);
        final int cantSinonimos = tmpSinonimos.size();
        for(int i = 0; i < cantSinonimos + 1; i++) {
            diccionario.put(palabra, tmpSinonimos);
            tmpSinonimos.add(palabra);            
            palabra = tmpSinonimos.remove(0);
        }
    }
    
    /**
     * Remueve una palabra del dicionario. Si la palbra no
     * existe ninguna accion se lleva a cabo
     * 
     * @param palabra palabra a remover.
     * @thorws IllegalArgumentEception si palabra es null.
     */
    public void remove(String palabra) {
        if(palabra == null) {
             throw new IllegalArgumentException("parametro palabra no puede ser nulo");
        }

        //si guaarda aca los sinonimos para luego ir por cada uno de ellos y borrar a palabra de sus listas de sinonimos.
        List<String> sinonimos = diccionario.get(palabra).getSinonimos();
        diccionario.remove(palabra);

        if(sinonimos != null) {
            for(String sinonimo : sinonimos) {
                removeSinonimoDe(sinonimo, palabra);
            }
        }

        if(observer != null) {
            observer.update(this);
        }        
    }
    
    /**
     * Remueve de la lista de sinonimos de una palabra
     * el sinonimo indicado. Si la lista de sinonimos no
     * no contiene al sinonimo ninguna accion se lleva a cabo.
     * 
     * @param palabra palabra a la caul removerle el sinonimo.
     * @param sinonimo sinonimo a remover.
     */
    public void removeSinonimoDe(String palabra, String sinonimo) {
        EntradaDiccionario entrada = diccionario.get(palabra);
        if(entrada == null) {
            return;
        }
        
        entrada.removeSinonimo(sinonimo);
    }

    /**
     * Setea el observador del diccionario.
     * 
     * @param observer
     */
    public void setOberver(IObserver observer) {
        this.observer = observer;
    }
    
   public IObserver getObserver() {
        return observer;
    }
   
    public void removeObserver() {
        this.observer = null;
    }

    /**
     * Remueve todas las palabras del diccionario.
     */
    void removeAll() {
       diccionario.removeAll();
       
        if(observer != null) {
            observer.update(this);
        }       
    }
    
    /**
     * Obtiene la lista de sinonimos de una palabra.
     * 
     * @param palabra palabra de la cual se quiere obtener la lista de sinonimos
     * @return la lista de sinonimos o null si no existe la palabra en el diccionario.
     */
    public List<String> getSinonimosDe(String palabra) {
        EntradaDiccionario entrada = diccionario.get(palabra);
        if(entrada == null) {
            return null;
        }
        
        return entrada.getSinonimos();
    }
    
    /**
     * Obtiene la cantidad de palabras del diccinario. No se cuentan
     * los sinonimos de las palabras.
     * 
     * @return cantidad de palabras del diccionario.
     */
    public int size() {
        return diccionario.size();
    }
    
    public String toString() {
        return diccionario.toString();
    }
    
    /**
     * Obtiene informacion de factor de carga y colisones.
     * @return una cadena con la informacion.
     */
    public String printDebugInfo() {
        return diccionario.printDebugInfo();
    }
}
